package cn.scy.business.service.guangzlt;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.Log;
import cn.scy.base.PageResult;
import cn.scy.business.domain.entity.NumMemberInfo;
import cn.scy.business.domain.entity.guangzlt.NumNumberInfo;
import cn.scy.business.domain.entity.guangzlt.NumReportInfo;
import cn.scy.business.domain.param.guangzlt.CustomerNumReq;
import cn.scy.business.domain.param.Member;
import cn.scy.business.domain.param.guangzlt.NotifyCustomerReq;
import cn.scy.business.domain.param.guangzlt.NumNumberInfoBatchParam;
import cn.scy.business.domain.param.guangzlt.NumNumberInfoParam;
import cn.scy.business.domain.vo.NotifyResult;
import cn.scy.business.mapper.guangzlt.NumNumberInfoMapper;
import cn.scy.business.mapper.guangzlt.NumReportInfoMapper;
import cn.scy.business.service.NumMemberInfoService;
import cn.scy.constant.BusinessConst;
import cn.scy.exception.BusinessException;
import cn.scy.util.MD5Utils;
import cn.scy.util.PageUtil;
import cn.scy.util.SignUtils;
import cn.scy.util.Sm4Util;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 报备管理-号码表 服务实现类
 *
 * @author scy
 * @since 2025-04-27
 */
@Service
public class NumNumberInfoServiceImpl extends ServiceImpl<NumNumberInfoMapper, NumNumberInfo> implements NumNumberInfoService {

    private static final Log LOGGER = Log.get();

    @Resource
    private NumMemberInfoService memberInfoService;

    @Resource
    private NumReportInfoMapper reportInfoMapper;

    @Value("${numReport.appId}")
    private String appId;

    @Value("${numReport.appKey}")
    private String appKey;

    @Value("${numReport.apiUrl}")
    private String apiUrl;

    @Override
    public PageResult<NumNumberInfo> listPage(NumNumberInfoParam param) {
        // 构建分页对象
        Page<NumNumberInfo> pageQuery = PageUtil.convert2PageQuery(param);
        // 构建查询参数对象
        LambdaQueryWrapper<NumNumberInfo> query = new LambdaQueryWrapper<>();
        query.eq(StrUtil.isNotBlank(param.getMemberId()), NumNumberInfo::getMemberId, param.getMemberId());
        query.like(StrUtil.isNotBlank(param.getPhoneNumber()), NumNumberInfo::getPhoneNumber, param.getPhoneNumber());
        query.eq(ObjectUtil.isNotNull(param.getPhoneType()), NumNumberInfo::getPhoneType, param.getPhoneType());
        query.eq(StrUtil.isNotBlank(param.getStatus()), NumNumberInfo::getStatus, param.getStatus());
        query.orderByDesc(NumNumberInfo::getCreateTime).orderByAsc(NumNumberInfo::getId);
        // 执行查询
        Page<NumNumberInfo> pageData = this.page(pageQuery, query);
        return PageUtil.convert2PageResult(pageData, pageData.getRecords());
    }

    @Override
    public NumNumberInfo info(String id) {
        return this.getById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addNumber(NumNumberInfo numNumberInfo) {
        // 校验参数
        checkParam(numNumberInfo);
        // 保存数据
        return this.save(numNumberInfo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addNumberBatch(NumNumberInfoBatchParam params) {
        if (ObjectUtil.isNull(params)) {
            throw new BusinessException("参数不能为空");
        }
        List<String> numbers = params.getNumbers();
        if (CollUtil.isEmpty(numbers)) {
            throw new BusinessException("号码不能为空");
        }
        // 号码去重
        numbers = CollUtil.distinct(numbers);
        // 校验数据
        List<NumNumberInfo> list = CollUtil.newArrayList();
        numbers.forEach(phoneNumber -> {
            NumNumberInfo num = new NumNumberInfo();
            num.setMemberId(params.getMemberId());
            num.setPhoneNumber(phoneNumber);
            num.setPhoneType(params.getPhoneType());
            checkParam(num);
            list.add(num);
        });
        // 保存数据
        return this.saveBatch(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editNumber(NumNumberInfo numNumberInfo) {
        // 校验参数
        checkParam(numNumberInfo);
        // 更新数据
        return this.updateById(numNumberInfo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteNumber(String id) {
        // 校验是否有请求记录
        boolean exists = reportInfoMapper.exists(
                new LambdaQueryWrapper<NumReportInfo>()
                        .eq(NumReportInfo::getNumberId, id)
        );
        if (exists) {
            throw new BusinessException("该号码名下已有报备请求记录，无法删除");
        }
        // 删除数据
        return this.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean report(NumReportInfo numReportInfo) {
        NumNumberInfo numberInfo = this.getById(numReportInfo.getNumberId());
        // 校验参数
        checkNum(numReportInfo, numberInfo);
        NumMemberInfo memberInfo = memberInfoService.getById(numberInfo.getMemberId());

        // 新增请求记录
        String requestId = IdUtil.simpleUUID();
        numReportInfo.setRequestId(requestId);
        reportInfoMapper.insert(numReportInfo);

        // 准备参数
        CustomerNumReq req = new CustomerNumReq();
        req.setRequestId(requestId);
        req.setMethod(numReportInfo.getMethod());
        req.setReportType(numReportInfo.getReportType());
        req.setPhoneType(numberInfo.getPhoneType());

        // 构造请求内容，每次报备传1个
        List<Member> members = new ArrayList<>();
        Member member = new Member();
        member.setPhoneNumber(numberInfo.getPhoneNumber());
        member.setName(memberInfo.getName());
        // 身份证号
        member.setIdentityCard(memberInfo.getIdentityCard());
        // 只需提供人脸照片即可
        member.setLiveFaceImage(memberInfo.getLiveFaceImage());
        members.add(member);
        // sm4密钥：将 appKey 转成 md5 再取中间 16 位
        String sm4Key = MD5Utils.get16MD5(appKey);
        try {
            // 加密
            String membersEncrypted = Sm4Util.encryptEcb(sm4Key, JSONObject.toJSONString(members));
            // member 不参与签名
            SignUtils.sign(req, appId, appKey);
            // 设置加密后的 member
            req.setMember(membersEncrypted);
        } catch (Exception e) {
            throw new BusinessException("加密失败");
        }
        LOGGER.info("请求体 " + JSONObject.toJSONString(req));
        // 发起请求
        String postResult = HttpUtil.post(apiUrl, JSONObject.toJSONString(req));
        LOGGER.info("响应体 " + postResult);
        // 解析响应
        JSONObject postResultJson = JSONObject.parseObject(postResult);
        if (ObjectUtil.equals(200, postResultJson.getInteger("code"))) {
            // 更新号码状态为待核验
            numberInfo.setStatus("1");
            this.updateById(numberInfo);
            // 更新报备记录
            numReportInfo.setReportStatus(BusinessConst.REPORT_SUCCESS);
            reportInfoMapper.updateById(numReportInfo);
            return true;
        } else {
            throw new BusinessException(postResultJson.getString("msg") + "，号码：" + numberInfo.getPhoneNumber() + "，报备操作：" + numReportInfo.getMethod());
        }
        //return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> notifyReq(NotifyCustomerReq req) {
        // 联通发来的核验结果
        LOGGER.info("接收到联通发来的核验结果：" + JSONObject.toJSONString(req));
        LOGGER.info("开始处理核验结果");
        Map<String, Object> response = new HashMap<>(2);
        // 校验 requestId
        String requestId = req.getRequestId();
        NumReportInfo numReportInfo = reportInfoMapper.selectOne(
                new LambdaQueryWrapper<NumReportInfo>()
                        .eq(NumReportInfo::getRequestId, requestId)
        );
        if (ObjectUtil.isNull(numReportInfo)) {
            LOGGER.error("找不到对应的报备记录，requestId：" + requestId);
            response.put("code", 500);
            response.put("msg", "requestId 有误");
            return response;
        }
        // 核验结果
        String result = req.getResult();
        // 回调时解密的密钥取appKey前16位
        String sm4Key = appKey.substring(0, 16);
        List<NotifyResult> notifyResults;
        try {
            // 解密
            String resultDecrypted = Sm4Util.decryptEcb(sm4Key, result);
            // 核验结果
            notifyResults = JSONObject.parseObject(resultDecrypted, new TypeReference<List<NotifyResult>>() {
            });
            LOGGER.info("核验结果 " + JSONObject.toJSONString(notifyResults));
        } catch (Exception e) {
            LOGGER.error("核验结果解密失败", e);
            response.put("code", 500);
            response.put("msg", "核验结果解密失败");
            return response;
        }
        // 更新号码状态和报备记录等本地数据
        if (CollUtil.isNotEmpty(notifyResults)) {
            updateLocalInfo(notifyResults, numReportInfo);
        }
        response.put("code", 200);
        response.put("msg", "核验结果接收成功");
        LOGGER.info("核验结果处理完成");
        return response;
    }

    private void updateLocalInfo(List<NotifyResult> notifyResults, NumReportInfo numReportInfo) {
        NotifyResult notifyResult = notifyResults.get(0);
        // 更新号码状态
        NumNumberInfo number = this.getById(numReportInfo.getNumberId());
        if (ObjectUtil.isNotNull(notifyResult.getStatus()) && ObjectUtil.equals(0, notifyResult.getStatus())) {
            if (StrUtil.equals(numReportInfo.getMethod(), "append")) {
                number.setStatus("2");
            }
            if (StrUtil.equals(numReportInfo.getMethod(), "delete")) {
                number.setStatus("4");
            }
        } else {
            if (StrUtil.equals(numReportInfo.getMethod(), "append")) {
                number.setStatus("3");
            }
            if (StrUtil.equals(numReportInfo.getMethod(), "delete")) {
                number.setStatus("5");
            }
            numReportInfo.setNotifyFailReason(notifyResult.getFailReason());
        }
        this.updateById(number);
        // 更新报备记录
        numReportInfo.setNotifyStatus(Convert.toStr(notifyResult.getStatus()));
        numReportInfo.setNotifyTime(DateUtil.date());
        reportInfoMapper.updateById(numReportInfo);
    }

    private void checkNum(NumReportInfo numReportInfo, NumNumberInfo numberInfo) {
        // 如果号码已经被报备，或者处于待核验状态下，则不允许再次报备
        String status = numberInfo.getStatus();
        if (StrUtil.equalsAny(status, "1", "2") && StrUtil.equals(numReportInfo.getMethod(), "append")) {
            throw new BusinessException("该号码已被追加或处于待核验状态，无法再次追加");
        }
        if (StrUtil.equalsAny(status, "1", "4") && StrUtil.equals(numReportInfo.getMethod(), "delete")) {
            throw new BusinessException("该号码已被删除或处于待核验状态，无法再次删除");
        }
    }

    private void checkParam(NumNumberInfo numNumberInfo) {
        // 同一人名下不能有重复号码
        LambdaQueryWrapper<NumNumberInfo> query = new LambdaQueryWrapper<>();
        query.eq(StrUtil.isNotBlank(numNumberInfo.getMemberId()), NumNumberInfo::getMemberId, numNumberInfo.getMemberId());
        query.eq(StrUtil.isNotBlank(numNumberInfo.getPhoneNumber()), NumNumberInfo::getPhoneNumber, numNumberInfo.getPhoneNumber());
        query.ne(StrUtil.isNotBlank(numNumberInfo.getId()), NumNumberInfo::getId, numNumberInfo.getId());
        long count = this.count(query);
        if (ObjectUtil.isNotNull(count) && count > 0) {
            throw new BusinessException("同一人名下不能有重复号码，重复号码：" + numNumberInfo.getPhoneNumber());
        }
    }

}
